// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.IO;
using System.Collections;
using System.Xml;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.Reflection;
using CIP4.Common;

namespace CIP4.RulesEngine
{
    public partial class ActionSequenceType : BaseActionType
    {
        private List<BaseActionType> m_ActionsCollection;
        /// <summary>
        /// Multi valued ConditionActionType object value collection property: maxOccurs 79228162514264337593543950335 not required 
        /// 
        /// </summary>
        public List<BaseActionType> Actions
        {
            get
            {
                if (m_ActionsCollection == null)
                    m_ActionsCollection = new List<BaseActionType>();
                return m_ActionsCollection;
            }
        }

        public IDataPacketType Data;
        public ActionSequenceType() { }
        public ActionSequenceType(IDataPacketType dataPacket)
        {
            Data = dataPacket;
        }
        public ActionSequenceType(List<BaseActionType> actions)
        {
            Actions.AddRange(actions);
        }
        public ActionSequenceType(XElement node, BaseActionType owner)
        {
            ObjectOwner = owner;
            Initialize(node);
        }
        protected virtual void Initialize(XElement node)
        {
            if (node == null || node.Name != XName.Get("ActionSequence", RuleXmlNS))
                throw new ArgumentException("An ActionSequence can only be constructed from a ActionSequence Xml node");
            XElement originNode = node.Element(XName.Get("Origin", RuleXmlNS));
            if (originNode != null)
                Origin = originNode.Value == "FromCurrentType" ? ActionOriginType.FromCurrentType : ActionOriginType.FromPropertyType;
            XAttribute usageNode = node.Attribute(XName.Get("Usage", RuleXmlNS));
            if (usageNode != null)
                Usage = ActionUsageEnumFromString(usageNode.Value);
            XElement descriptionNode = node.Element(XName.Get("Description", RuleXmlNS));
            if (descriptionNode != null)
                Description = descriptionNode.Value;
            InitializeParameters(node);
            foreach (XElement actionNode in node.Elements())
                if (!(actionNode.Name == XName.Get("Origin", RuleXmlNS) ||
                    actionNode.Name == XName.Get("Usage", RuleXmlNS) ||
                    actionNode.Name == XName.Get("Description", RuleXmlNS) ||
                    actionNode.Name == XName.Get("FormalParameters", RuleXmlNS)))
                    Actions.Add(BaseActionType.Initialize(actionNode, this));
        }
        public void Execute()
        {
            if (Data != null && Data.PacketStats != null)
                Data.PacketStats.TestedObjectCount += 1;
            foreach (BaseActionType ca in Actions)
                ca.Execute(Data);
        }
        public override void Execute(IDataPacketType data)
        {
            Data = data;
            Execute();
        }
        public bool Check()
        {
            return Check(new RulesEngineDataPacket());
        }
        public override bool Check(IDataPacketType data)
        {
            bool result = true;
            Data = data;
            foreach (BaseActionType ca in Actions)
                result = result && ca.Check(Data);
            return result;
        }
        public override void XmlFor(XmlWriter xwrtr, bool writeXmlNamespace)
        {
            xwrtr.WriteStartElement("ActionSequence");
            if (writeXmlNamespace)
                xwrtr.WriteAttributeString("xmlns", RuleXmlNS);
            if (Origin.HasValue)
                xwrtr.WriteElementString("Origin", Origin.ToString());
            if (Usage.HasValue && Usage != ActionUsageEnum.None)
                xwrtr.WriteAttributeString("Usage", Usage.ToString());
            if (!string.IsNullOrEmpty(Description))
                xwrtr.WriteElementString("Description", Description);
            XmlForFormalParameters(xwrtr);
            foreach (BaseActionType ca in Actions)
                ca.XmlFor(xwrtr, false);
            xwrtr.WriteEndElement();
        }
        /// <summary>
        ///  Rules may be defined in one context and then used in another. For example a rule might be 
        ///  defined on a type which is then used in the context of several different properties. The 
        ///  ResolveNames method goes through all the Atoms in a rule looking for Atoms that have an 
        ///  AtomSourceType == NamedValue and qualifies the name with the contextname  - the rules must 
        ///  be copied before calling ResolveNames by executing the following 
        ///   
        ///      ActionSequenceType newRule = new ActionSequenceType(originalRule.XmlFor());
        ///      
        ///  The qualified names might look like this:
        ///   
        ///      p1 - for a single valued property at the root of the object that is the scope of the conditionActionSequence
        ///      p1.p2 - for a single valued property in a compound property - the p1, p2's match the actual property names
        ///      p1[index1] for a multi valued property at the root
        ///      p1[index1].p2.p3[index2] - you get the idea...
        ///   
        ///  The following illustrates what the name qualification does. Assume a PriceType is defined with the 
        ///  rule that a price must be greater than zero. When the rule is defined the name of the property is 
        ///  not known and the rule will be expressed as follows:
        ///   
        ///          PriceType 
        ///              (unbound variable > 0)
        ///   
        ///  Assume a ProductType is defined with a RegularPrice property of type PriceType and a SalePrice 
        ///  property also of type PriceType. Rules must be attached to the ProductType that verifies the 
        ///  RegularPrice and the SalePrice in the context of a product. The name used in the context of 
        ///  the orginal PriceType must be changed to reflect the names where the PriceType is being used resulting in:
        ///   
        ///      ProductType 
        ///          (unbound variable.RegularPrice > 0)
        ///          (unbound variable.SalePrice > 0)
        ///                      RegularPrice: PriceType 
        ///                      SalePrice: PriceType 
        ///   
        ///   
        ///  If the ProductType is then used in the context of a CampaignType. The rules will have to be attached 
        ///  to the CampaignType to verify the Price properties in that context
        ///   
        ///  Campaign 
        ///          (unbound variable.CampaignProduct.RegularPrice > 0)
        ///          (unbound variable.CampaignProduct.SalePrice > 0)
        ///              CampaignProduct: Product
        ///                                                  Price: PriceType
        ///                                                  
        ///  In Binding to a new context the algorithm requires the first part of the name (the literal "unbound 
        ///  variable") to be replaced by the context name preceded by "unbound variable." (note the period suffix)
        ///   
        ///  At some point the type will be used in the context of a collection or record rather than another type. 
        ///  At this point the "unbound variable" is dropped as the rule will now be bound to an actual value and can be executed.
        ///   
        ///  Mapping rules are rather more complex as they involve two records not just one but follow the same general pattern.
        ///   
        ///  
        /// </summary>
        /// <param name="qualifiedName">Fully qualified name to substitute</param>
        public override void ResolveName(string qualifiedName)
        {
            foreach (BaseActionType action in Actions)
                action.ResolveName(qualifiedName);
        }
        public void Merge(BaseActionType rule)
        {
            if (rule == null)
                return;
            if (rule is ActionType)
                Actions.Add(rule);
            else if (rule is ConditionalActionType)
                Actions.Add(rule);
            else if (rule is ActionSequenceType)
                foreach (BaseActionType action in ((ActionSequenceType)rule).Actions)
                    Actions.Add(action);
            else
                throw new ArgumentException(string.Format("Don't know how to merge rule like {0}", rule.ToString()));
        }
        public override BaseActionType Copy()
        {
            return new ActionSequenceType(XElement.Parse(XmlFor()), null);
        }
        public override string ToString(string indent)
        {
            string s = "";
            string sep = "";
            if (!string.IsNullOrEmpty(Description))
            {
                s += indent + "' " + Description;
                sep = Environment.NewLine + indent;
            }
            foreach (BaseActionType ca in Actions)
            {
                s += sep + ca.ToString(indent);
                sep = Environment.NewLine;
            }
            return s;
        }
        public override string ToString()
        {
            return ToString("");
        }
    }
}
